package com.demo.bean;

import com.demo.aop.advisor.Advisor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegistry {

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    private Map<String, Object> singletonMap = new ConcurrentHashMap<>(256);
    private ThreadLocal<Set<String>> creatingBean = new ThreadLocal<Set<String>>(){
        @Override
        protected Set<String> initialValue() {
            return new HashSet(64);
        }
    };

    private List<Advisor> advisors;

    private List<BeanPostProcessor> beanPostProcessors;

    public DefaultBeanFactory(){
        advisors = new ArrayList<>(10);
        beanPostProcessors = new ArrayList<>(10);
    }

    @Override
    public void registryBeanDefinition(String name, BeanDefinition beanDefinition) {
        if(beanDefinitionMap.containsKey(name)){
            throw new RuntimeException("bean名称冲突:[name = " + name + "]");
        }
        beanDefinitionMap.put(name, beanDefinition);
    }

    @Override
    public BeanDefinition getBeanDefinition(String name) {
        return beanDefinitionMap.get(name);
    }

    @Override
    public Object getBean(String name) throws Exception {
        return this.doGetBean(name);
    }

    private Object doGetBean(String name) throws Exception {
        //缓存在如果有，直接返回
        if(singletonMap.containsKey(name)){
            return singletonMap.get(name);
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if(beanDefinition == null){
            throw new RuntimeException("bean:"+name+"不存在！");
        }
        if(creatingBean.get().contains(name)){
            throw new RuntimeException("发现循环依赖,请检查.正在创建的bean:"+creatingBean.get().toString()+",当前循环的bean:"+name);
        }
        creatingBean.get().add(name);
        Object instance = null;
        if(beanDefinition.getBeanClass()==null){
            if(beanDefinition.getFactoryClass()!=null){
                instance = this.createBeanByStaticFactory(beanDefinition);
            }else if(beanDefinition.getFactoryBeanName()!=null){
                instance = this.createBeanByFactoryMethod(beanDefinition);
            }
        }else{
            instance = this.createBeanByConstructor(beanDefinition);
        }
        if(instance == null){
            throw new RuntimeException("创建bean实例异常!");
        }
        //移除正在创建的bean
        creatingBean.get().remove(name);
        //装载属性
        this.loadingProperty(beanDefinition,instance);
        instance = invokeBeforBeanPostProcessor(name,instance);
        //执行初始化方法
        this.invokeInitMethod(beanDefinition,instance);
        instance = invokeAfterBeanPostProcessor(name,instance);
        if(beanDefinition.isSingleton()){
            singletonMap.put(name, instance);
        }
        return instance;
    }

    private Object invokeBeforBeanPostProcessor(String beanname,Object instance) throws Exception {
        for (BeanPostProcessor beanPostProcessor : this.getBeanPostProcessors()) {
            instance = beanPostProcessor.beforeBeanInitMethod(this,beanname,instance);
        }
        return instance;
    }

    private Object invokeAfterBeanPostProcessor(String beanname,Object instance) throws Exception {
        for (BeanPostProcessor beanPostProcessor : this.getBeanPostProcessors()) {
            instance = beanPostProcessor.afterBeanInitMethod(this,beanname,instance);
        }
        return instance;
    }

    private void invokeInitMethod(BeanDefinition beanDefinition, Object instance) throws Exception {
        String initMethodName = beanDefinition.getInitMethodName();
        if(initMethodName != null){
            instance.getClass().getMethod(initMethodName).invoke(instance);
        }
    }

    private void loadingProperty(BeanDefinition beanDefinition, Object instance) throws Exception {
        List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();
        if(propertyValues != null){
            for (PropertyValue propertyValue : propertyValues) {
                String name = propertyValue.getName();
                if( name == null){
                    continue;
                }
                Class<?> clazz = instance.getClass();
                Field field = clazz.getDeclaredField(name);
                field.setAccessible(true);
                Object value = parseRealValue(propertyValue.getValue());
                field.set(instance,value);
            }
        }
    }

    private Object parseRealValue(Object value) throws Exception {
        if(value == null){
            return null;
        }else if(value instanceof Object[]){
            //TODO 具体处理逻辑待实现
            return null;
        }else if(value instanceof Collection){
            //TODO 具体处理逻辑待实现
            return null;
        }else if(value instanceof Properties){
            //TODO 具体处理逻辑待实现
            return null;
        }else if(value instanceof Map){
            //TODO 具体处理逻辑待实现
            return null;
        }else if(value instanceof BeanRefrence){
            return this.doGetBean(((BeanRefrence) value).getName());
        }else {
            return value;
        }
    }


    private Object createBeanByFactoryMethod(BeanDefinition beanDefinition) throws Exception {
        String factoryBeanName = beanDefinition.getFactoryBeanName();
        Object factoryObj = this.doGetBean(factoryBeanName);
        Object[] values = parseConstructorArgumentValues(beanDefinition.getConstructorArgumentValues());
        if(beanDefinition.getFactoryMethod() != null){
            return beanDefinition.getFactoryMethod().invoke(factoryObj, values);
        }
        Method method = findMethod(factoryObj.getClass(),beanDefinition.getFactoryMethodName(),values);
        beanDefinition.setFactoryMethod(method);
        return method.invoke(factoryObj,values);
    }

    private Object createBeanByStaticFactory(BeanDefinition beanDefinition) throws Exception {
        Object[] values = parseConstructorArgumentValues(beanDefinition.getConstructorArgumentValues());
        if(beanDefinition.getFactoryMethod() != null){
            return beanDefinition.getFactoryMethod().invoke(null, values);
        }
        String factoryClassName = beanDefinition.getFactoryClass();
        Class<?> factoryClass = Class.forName(factoryClassName);
        Method method = findMethod(factoryClass,beanDefinition.getFactoryMethodName(),values);
        beanDefinition.setFactoryMethod(method);
        return method.invoke(null,values);
    }

    private Method findMethod(Class clazz,String methodName,Object[] params){
        Method[] methods = clazz.getMethods();
        Method realMethod = null;
        out:
        for (Method method : methods) {
            if(method.getName().equals(methodName) && method.getParameterCount() == params.length){
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    if(!parameterTypes[i].isAssignableFrom(params[i] == null?null:params[i].getClass())){
                        continue out;
                    }
                }
                realMethod = method;
                break;
            }
        }
        if(realMethod ==null){
            throw new RuntimeException("没有找到对应的方法!");
        }
        return realMethod;
    }


    private Object createBeanByConstructor(BeanDefinition beanDefinition) throws Exception {
        Object[] values = parseConstructorArgumentValues(beanDefinition.getConstructorArgumentValues());
        Constructor cacheConstructor = beanDefinition.getConstructor();
        if(cacheConstructor!=null){
            return cacheConstructor.newInstance(values);
        }

        String beanClassStr = beanDefinition.getBeanClass();
        Class<?> beanClass = Class.forName(beanClassStr);
        Constructor realConstructor = null;
        //获取无参构造
        if(values == null){
            realConstructor = beanClass.getConstructor();
            beanDefinition.setConstructor(realConstructor);
            return realConstructor.newInstance();
        }
        //如果有参构造则先匹配数量，再匹配类型
        Constructor<?>[] constructors = beanClass.getConstructors();
        out:
        for (Constructor<?> constructor : constructors) {
            if(constructor.getParameterCount() == values.length ){
                Class<?>[] parameterTypes = constructor.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    if(!parameterTypes[i].isAssignableFrom(values[i] == null ? null : values[i].getClass())){
                        continue out;
                    }
                }
                realConstructor = constructor;
                break;
            }
        }
        if(realConstructor != null){
//            if(beanDefinition.isPrototype()){
                beanDefinition.setConstructor(realConstructor);
//            }
            return realConstructor.newInstance(values);
        }else{
            throw new RuntimeException("未找到对应的构造器!");
        }
    }

    private Object[] parseConstructorArgumentValues(List<Object> constructorArgumentValues) throws Exception {
        if(constructorArgumentValues == null){
            return null;
        }
        Object[] values = new Object[constructorArgumentValues.size()];
        for (int i=0;i<values.length; i++) {
            Object value = constructorArgumentValues.get(i);
            if(value == null){
                values[i] = null;
            }else if(value instanceof Object[]){
                //TODO 具体处理逻辑待实现
            }else if(value instanceof Collection){
                //TODO 具体处理逻辑待实现
            }else if(value instanceof Properties){
                //TODO 具体处理逻辑待实现
            }else if(value instanceof Map){
                //TODO 具体处理逻辑待实现
            }else if(value instanceof BeanRefrence){
                values[i] = this.doGetBean(((BeanRefrence)value).getName());
            }else {
                values[i] = value;
            }
        }
        return values;
    }


    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = this.getBean(name);
        if(clazz.isAssignableFrom(bean.getClass())){
            return (T)bean;
        }
        throw new RuntimeException("根据bean名称获取到的bean的类型跟传入的参数不一致");
    }

    @Override
    public boolean containBean(String name) {
        return beanDefinitionMap.containsKey(name);
    }

    @Override
    public boolean isSingleton(String name) {
        return beanDefinitionMap.get(name).isSingleton();
    }

    @Override
    public boolean isPrototype(String name) {
        return beanDefinitionMap.get(name).isPrototype();
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        beanPostProcessors.add(beanPostProcessor);
    }

    public List<BeanPostProcessor> getBeanPostProcessors() {
        return beanPostProcessors;
    }

    @Override
    public void registryAdvisor(Advisor advisor) {
        advisors.add(advisor);
    }

    @Override
    public List<Advisor> getAdvisors() {
        return advisors;
    }
}
